package cn.pink.core.gen.entity;

import cn.pink.core.gen.GenBase;
import cn.pink.core.gen.GenUtils;
import cn.pink.core.support.ClassFinder;
import cn.pink.core.support.Utils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Entity自动生成类
 */
public class GenEntity extends GenBase {
	public static final String TEMP_NAME = "Entity.ftl";
	public static final String TEMP_NAME_SUPER = "SuperEntity.ftl";
	
	private static final String GENERATE_BEGIN = "GENERATE BEGIN";
	private static final String GENERATE_END = "GENERATE END";
	private static final String DBReferenceTable = "DBReferenceTable";
	
	/**
	 *  创建和调整配置,这个在生命周期中只做一次
	 */
	public GenEntity(String packageName, String targetDir) {
		super(packageName, targetDir);
		this.init();
	}
	
	/**
	 * 初始化class信息，是否能生成等字段
	 */
	private void init() {
		/**  默认是true */
		this.canGen = true;
		
		// 遍历所有类信息, 获取模板信息, 判断是否出错, 出错则初始化异常不能生成
		List<Class<?>> classes = getClassInfoToGen();
		for ( Class<?> clazz : classes) {
			try {
				
				this.rootMaps.add(getRootMap(clazz));
			} catch (Exception e) {
				// 如果获取模板内容异常，表示不能生成
				/**  不能生成 */
				this.canGen = false;
				System.err.println("文件存在错误，不继续进行Entity生成了，错误如下：");
				e.printStackTrace();
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	protected void genGlobalFileHandler(Map<String, Object> rootMaps) throws Exception {

	}

	/**
	 * 生成java实体类的核心方法
	 * @param rootMap
	 * @throws Exception
	 */
	@Override
	protected void genFileHandler(Map<String, Object> rootMap) throws Exception {
		// 准备参数生成文件
		String fileName = (String) rootMap.get("entityName");
		String packName = (String) rootMap.get("packageName");
		String tempFile = (String) rootMap.get("templateName");
		String targetDir = this.targetDir + ClassFinder.packageToPath(packName) + "/";
		
		Utils.freeMarker(TEMP_DIR, tempFile, rootMap, targetDir, fileName + ".java");
	}
	
	/**
	 * 获取要生成的class信息
	 * @return
	 */
	public List<Class<?>> getClassInfoToGen() {
		List<Class<?>> result = new ArrayList<>();
		
		// 获取源文件夹下的所有类
		List<Class<?>> sources = ClassFinder.getAllClass(packageName);
		
		// 遍历所有类，取出有注解的生成实体类
		for (Class<?> clazz : sources) {
			// 过滤没有@Entity注解的类
			if (clazz.isAnnotationPresent(Entity.class)) {
				result.add(clazz);
			}
		}
		
		return result;
	}
	
	/**
	 * 根据Class获取填充模板内容
	 * @param clazz
	 * @return
	 * @throws Exception 
	 */
	private Map<String, Object> getRootMap(Class<?> clazz) throws Exception {
		/* 设置模板的数据内容 */
		Map<String, Object> rootMap = new HashMap<>();
		List<Map<String, Object>> fields = new ArrayList<>();
		
		// 模板名
		String templateName = TEMP_NAME;

		// 获取实体类名,表名,包名
		String packageName = clazz.getPackage().getName();
		String entityName = (String) GenUtils.getEntityField(clazz, "entityName");
		String tableName = (String) GenUtils.getEntityField(clazz, "tableName");
		
		// 填充Map
		rootMap.put("packageName", packageName);
		rootMap.put("entityName", entityName);
		rootMap.put("tableName", tableName);
		rootMap.put("fields", fields);
		rootMap.put("templateName", templateName);

		// 默认加入id的get和set信息，id这玩意儿在枚举类里面木有，所以在这儿处理
//		Map<String, Object> id = new HashMap<>();
//		id.put("isTransient", false);
//		id.put("name", "_id");
//		id.put("type", "String");
//		id.put("comment", "id");
//
//		fields.add(id);
		
		// 获得所有枚举字段成员（id, account, name, profession...），
		// 遍历每个枚举成员，获取属性，放入Map中
		Field[] enums = clazz.getDeclaredFields();
		for (Field f : enums) {
			Map<String, Object> field = new HashMap<>();
			String name = f.getName();

			String type = f.getType().getSimpleName();

			StringBuilder sb = null;
			StringBuilder generics = null;

			boolean isCollection = false;

			Type genericType = f.getGenericType();
			if(genericType instanceof ParameterizedType ptype) {
				isCollection = true;

				// 集合 取泛型
				List<Class<?>> t = new ArrayList<>();

				Type[] actualTypeArguments = ptype.getActualTypeArguments();
				for(Type tt : actualTypeArguments) {
					Class<?> c = (Class<?>) tt;
					t.add(c);
				}

				if(!t.isEmpty()) {
					sb = new StringBuilder().append("<");
					generics = new StringBuilder();

					for(int i = 0; i < t.size(); i++) {
						String typeName = t.get(i).getSimpleName();

						Entity entity = t.get(i).getAnnotation(Entity.class);
						if(entity != null) {
							typeName = entity.entityName();
						}

						sb.append(typeName);
						sb.append(",");

						generics.append(typeName).append(" ").append("param").append(i);
						generics.append(",");
					}

					sb.replace(sb.length() - 1, sb.length(), "");
					generics.replace(generics.length() - 1, generics.length(), "");

					sb.append(">");
				}
			}
			else {
				// 字段类型是entity 替换成db类型
				Entity entity = f.getType().getAnnotation(Entity.class);
				if(entity != null) {
					type = entity.entityName();
				}
			}

			String comment = "";
			String defaults = "";
			
			// 判断是否有Transient注解，在模板中进行相应的处理
			// 根据e获取所在class的对应name字段的注解是否有Transient
			if (GenUtils.isTransient(f)) {
				field.put("isTransient", true);
			} else {
				field.put("isTransient", false);
			}
			// 判断有默认值则加上
			if (!StringUtils.isEmpty(defaults)) {
				field.put("defaults", defaults);
			}

			field.put("name", name);
			field.put("type", type + (sb == null ? "" : sb.toString()));
			field.put("comment", comment);
			field.put("isCollection", isCollection);
			field.put("generics", generics == null ? "" : generics.toString());
			
			fields.add(field);
		}
		
		return rootMap;
	}
	
	public static void main(String[] args) throws Exception {
//		args = new String[2];

//		args[0] = "cn.pink.core.test";
//		args[1] = "/common/src/gen/java/";

		if (args.length < 2) {
			System.out.println("usage: package, targetDir");
			return;
		}
		
		//设置log4j2配置文件所需的环境变量，作用是gen的时候
		//不会报配置没找到的错误，同时有gen.log的日志记录
		System.setProperty("logFileName", "GenEntity");
		
		String packageName = args[0];
		String targetDir = System.getProperty("user.dir") + args[1];
		
		GenBase genEntity = new GenEntity(packageName, targetDir);
		
		// 判断不能生成Entity
		if (!genEntity.isCanGen()) {
			System.err.println("不能生成Entity，请检查重试。。");
			return;
		}
		
		// 测试生成实体类
		genEntity.genFiles();
	}
}
